#!/bin/bash

[ -t 0 ] || { exec ${XTERMINAL-alacritty} -e "$0" "$@"; }
# test: is this being executed inside a terminal?

# get a better impression of tiled images while browsing through
# folders full of them, recursively. change background color for transparent tiles,
# and use the resulting image as background (tiled).
#
# you may pass a different path as an argument, otherwise $dir will
# be searched for images.
#
# requires:
deps=(xprop feh find shuf which identify magick tput tabs yad)

# preview dimensions:
width=700
height=700

shopt -u nocasematch # just normalizing
################################################################################
# FUNCTIONS
function full_size_manip {
	# a set of manipulations that require a screen-sized canvas to be created from the tile.
	while true ; do
		screeninit "Submenu for ${array[c]}"
		#~printf "${red}No quality control for the imagemagick options! The defaults provide gaussian blur + radial gradient with low opacity (centered lighting). Also see: http://imagemagick.org/script/command-line-options.php$rst\n${blu}s:	Change canvas size (current: $tempres)\n${mag}b:	Toggle blur (current: $rst$rvs"
		printf "s:	Change canvas size (current: $tempres)
${mag}b:	Toggle blur ($toggleblur)"
		if [[ "$toggleblur" == "ON" ]]; then
			printf " - blur radius: $blurradius
\t1: Change Blur Radius
\t2: Change complete blur option"
		fi
		printf "\nl:	Toggle lighting ($togglelighting)\n"
		if [[ "$togglelighting" == "ON" ]]; then
			printf "\t3: Change lighting settings - currently:
\t   inner color: %s; outer color: %s
\t   opacity: %s; compose method: %s
\t4: Change complete lighting & composite option (tricky!)\n" "$innercolor" "$outercolor" "$opacity" "$composemethod"
		fi
		printf "r:	Redraw current preview
A:	Apply settings, then $message
q,Space:	Back to main menu
R:	Restore all values to their defaults\n"

			case $(prompt) in
			s)	printf "\nEnter canvas size, e.g. 1024x768, both width and height: min. 100, max 99999\n<Enter> to leave unchanged\n"
				until [[ "$x" =~ ^[0-9]{3,5}x[0-9]{3,5}$ ]] || [[ "x$x" == "x" ]]; do
				read -p ">>> " x
				done
				[[ "x$x" != "x" ]] && [[ "$x" != "r" ]] && tempres="$x"
			;;
			b)	[[ "$toggleblur" == "ON" ]] && toggleblur="OFF" || toggleblur="ON"
            ;;
            1)  if [[ "$toggleblur" == "ON" ]]; then
                    printf "\nEnter blur radius (current: $blurradius, <Enter> leaves it as it is): "
                    read x
                    [[ "x$x" != "x" ]] && blurradius="$x"
                    apply_values
                else
                    wrong_input
                fi
            ;;
            2)  if [[ "$toggleblur" == "ON" ]]; then
                    printf "\nEnter blur option (current: \"$blur\", <Enter> leaves it as it is): " 
                    read x
                    [[ "x$x" != "x" ]] && blur="$x"
                else
                    wrong_input
                fi
            ;;
			l)	[[ "$togglelighting" == "ON" ]] && togglelighting="OFF" || togglelighting="ON"
            ;;
			3)	if [[ "$togglelighting" == "ON" ]]; then
					printf "\nEnter inner color - "
					change_color "innercolor"
					printf "Enter outer color - "
					change_color "outercolor"
					read -p "Enter opacity (current: $opacity, <Enter> leaves it as it is): " x
					[[ "x$x" != "x" ]] && opacity="$x"
					read -p "Enter compose method (current: $composemethod, <Enter> leaves it as it is): " x
					[[ "x$x" != "x" ]] && composemethod="$x"
					apply_values
                else
                    wrong_input
				fi
			;;
			4)	if [[ "$togglelighting" == "ON" ]]; then
					printf "\nEnter lighting & composite option\ncurrent:\n\"$lighting\"\n<Enter> leaves it as it is: "
					read x
					[[ "x$x" != "x" ]] && lighting="$x"
                else
                    wrong_input
				fi
			;;
			A)	[[ "$togglelighting" == "OFF" ]] && lighting=""
				[[ "$toggleblur" == "OFF" ]] && blur=""
				echo
				if magick -size "$tempres" -quality 99 tile:"$tmptile" $blur $lighting "${output%.*}".jpg ; then
					set_bg full "${output%.*}.jpg"
				fi
			;;
			' '|q)
				return
			;;
			R)	set_defaults
			;;
			r)
			;;
			*)	wrong_input
			;;
			esac
	done
}
function change_color { # takes the name of the variable to be filled
	#~ printf "\ncurrent color: ${!1}\n"
	x=''
	until [[ "$x" =~ ^#([[:xdigit:]]{3}|[[:xdigit:]]{6}|[[:xdigit:]]{12})$ ]]; do
		#~ printf "New color in hex format #rgb or #rrggbb or #rrrrggggbbbb\nEnter manually, enter p to pick or just enter to cancel): "
		#~ x="$(prompt)"
		#~ [[ "$x" == "p" ]] && 
        x="$(yad --color --picker --init-color "${!1}" 2>/dev/null)"
        [ -z "$x" ] && return
	done
	declare -g $1="$x" # stackoverflow.com/a/27049256
}
function apply_values {
	lighting="( -size $tempres radial-gradient:${innercolor}-${outercolor} -spread 10 -alpha set -channel a -evaluate set $opacity +channel ) -compose $composemethod -composite"
	blur="-gaussian-blur $blurradius"
}
function set_defaults {
	tempres="$res"
	innercolor=white
	outercolor=black
	opacity="70%"
	composemethod=multiply
	togglelighting="ON"
	blurradius="4x2"
	toggleblur="OFF"
	apply_values
}
function drawimage {
	# http://blog.z3bra.org/2014/01/images-in-terminal.html
	#~ echo -e "0;1;0;$imgoffset;$imgwidth;$imgheight;2;2;;;$preview\n4;\n3;" | $ueberzug -x 0 -y 0 >/dev/null 2>&1
    # also see /usr/share/doc/w3m/README.img
    #~ {
        #~ printf "{"
        #~ printf '"%s": "%s", ' "action" "add" "identifier" "${0##*/}" "scaler" "distort" "x" "0" "y" "$offset" "width" "$COLUMNS" "height" "$((LINES-offset))" "path" "$preview"
        #~ printf '"%s": "%s" }\n' "synchronously_draw" "True"
    #~ } > "$UEBERZUG_FIFO"
    if [[ "$fehpid" == "" ]]; then
        feh --force-aliasing --no-menus --title "${0##*/} preview" --auto-reload --class halfscreen "$preview" & fehpid=$!
        sleep 0.1 && wmctrl -ia $WINDOWID
    fi
}
function exit_cleanup {
	clear
	rm -rf "$tmpdir"
    #~ rm -f "$UEBERZUG_FIFO"
    kill $fehpid >/dev/null 2>&1
    tput cnorm
    tabs
    exit
}
trap 'exit_cleanup' EXIT

function wrong_input {
	printf " - wrong input"
	sleep 0.3
	printf "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"
	tput el
}
function erase { # erases the character entered until end of line
	printf "\b"
	tput el
}
function set_bg {
	# 1st argument: action 2nd argument: imagefile
	case $1 in
	prepare)
		case $bg_setter in
		feh)
			cp -f "$fehbg" "$tmpdir/"
		;;
		fbsetbg)
			cp -f ~/.fluxbox/lastwallpaper "$tmpdir/"
		;;
		save)
		;;
		esac
	;;
	restore)
		case $bg_setter in
		feh)
			cp -f "$tmpdir/${fehbg##*/}" "$fehbg"
			. "$fehbg"
		;;
		fbsetbg)
			cp -f "$tmpdir/lastwallpaper" ~/.fluxbox/
			fbsetbg -l
		;;
		save)
		;;
		esac
	;;
	tiled)
		case $bg_setter in
		feh) feh --bg-tile "$2"
		;;
		fbsetbg) fbsetbg -t "$2"
		;;
		save)
		printf "background file saved as $2\n"
		;;
		esac
	;;
	full)
		case $bg_setter in
		feh)	feh --bg-fill "$2"
		printf "background set with $bg_setter\nfile saved as $2\nThis action can be reversed in the main menu.\n"
		anykey
		;;
		fbsetbg) fbsetbg -f "$2"
		printf "background set with $bg_setter\nfile saved as $2\nThis action can be reversed in the main menu.\n"
		anykey
		;;
		save)
		printf "background file saved as $2"
		if [ -e "${img_display%% *}" ]; then
			printf "\nPress any key to see full screen preview, q to skip "
			read -n1 x
			if [[ "$img_display" == */feh ]]; then
				[[ "$x" != "q" ]] && $img_display -N -q --info 'printf "Filename: %f\nSize: %wx%h - %S\nPress q or ESC to exit" ' -Y -F -N --draw-tinted "$2"
			else
				[[ "$x" != "q" ]] && $img_display "$2"
			fi
		fi
		;;
		esac
	;;
	esac
}
function clearstdin { # superuser.com/questions/276531/clear-stdin-before-reading
    while read -r -t 0; do read -r; done
	#~read -d '' -t 0.1 -n 10000
}
function anykey {
	read -n1 -p "Press any key to continue... "
}
function screeninit {
    clear
    drawimage
    #~tput cup 0 0
    #~tput sgr0
    printf "\033]0;%s\007" "$me - $1"
}
function prompt {
        local x
		oldifs="$IFS"
		IFS=$'\n'
		clearstdin
		read -n1 -p ">>> " x
		clearstdin
		IFS="$oldifs"
        printf "$x"
}
################################################################################
################################################################################
# MAIN

# define some default variables
me="$(basename $0)"
uuid="$me$(date +%y%m%d%H%M%S)"
alpha="$uuid" # just some control variable

outfile="$uuid.png"
outdir="${XDG_CONFIG_HOME-"$HOME/.config"}/tileset"
output="$outdir/$outfile"

tmpdir="/tmp/$uuid"
tmptile="$tmpdir/tmptile.png"
preview="$tmpdir/preview.png"

fehbg="${XDG_CONFIG_HOME-"$HOME/.config"}/.fehbg"

dir="$HOME/Pictures/tiles" # default directory if not specified as $1

shuffle=1 # randomize array
parse_hidden=0 # set to 1 to also process files and folders starting with a '.'
bgcol="#363D4D" # just some default color (greyish-blue)

for d in "${deps[@]}"; do
    type "$d" >/dev/null || exit 1
done

img_display="$(which feh 2>/dev/null)"

bg_setter=feh # one of: fbsetbg feh save
if [[ "$bg_setter" == "save" ]]; then message="save"; else message="set as desktop background"; fi

# screen resolution
res="$(xprop -notype -root '$0x$1' _NET_DESKTOP_GEOMETRY)"
res="${res##*_NET_DESKTOP_GEOMETRY}"

##### START DOING THINGS #####
mkdir -p "$outdir"
mkdir -p "$tmpdir"

set_bg prepare

#~ wmctrl -r :ACTIVE: -b remove,maximized_vert,maximized_horz
#~ wmctrl -r :ACTIVE: -e 0,-1,-1,$width,$height
tabs 10 +3 +3 +3 +3 +3 +3 +3 +3 # setting tabs - first is longer, rest is shorter than the default 8

# change to directory, if given on command line
if [ -d "$1" ] && [ -r "$1" ]; then
	dir="$1"
fi
cd "$dir" || exit 1

oldifs="$IFS"
IFS=$'\n'
# or, if a single file is given as argument, set array to that single file.
if [ -f "$1" ]; then
	array=("$1")
else
	if [[ "$parse_hidden" == "1" ]]; then
		array=($(find -iname '*.png' -print -o -iname '*.gif' -print -o -iname '*.jpg' -print -o -iname '*.bmp' -print -o -iname '*.jpe' -print -o -iname '*.jpeg' -print -o -iname '*.tga' -print -o -iname '*.tiff' -print -o -iname '*.xbm' -print -o -iname '*.xpm' -print))
	else # excludes hidden (dot) directories (and files?)
		array=($(find -path '*/.*' -prune -o -iname '*.png' -print -o -iname '*.gif' -print -o -iname '*.jpg' -print -o -iname '*.bmp' -print -o -iname '*.jpe' -print -o -iname '*.jpeg' -print -o -iname '*.tga' -print -o -iname '*.tiff' -print -o -iname '*.xbm' -print -o -iname '*.xpm' -print))
	fi
    if [[ "$shuffle" == 1 ]]; then
        array=( $(for i in "${array[@]}"; do echo "$i"; done | shuf) )
    fi
fi
IFS="$oldifs"

set_defaults

fehpid=""

# Setting up ueberzug
#~ export UEBERZUG_FIFO="$tmpdir/ueberzug.fifo"
#~ rm -f "$UEBERZUG_FIFO"
#~ mkfifo "$UEBERZUG_FIFO"
#~ ueberzug layer --parser json --loader synchronous < "$UEBERZUG_FIFO" &
#~ exec 3>"$UEBERZUG_FIFO"

###############################################################################
########## MAIN LOOP (UNTIL END OF ARRAY) ###########
for (( c=0 ; c< ${#array[*]} ; )); do
    size="$(identify -format "%wx%h" "${array[c]}")"
	if [[ "$alpha" == "$uuid" ]]; then
		# let's decide whether the image has an alpha channel or not:
		shopt -s nocasematch
		if [[ "${array[c]}" == *.jp? ]] || [[ "${array[c]}" == *.jpeg ]]
		then
			alpha=False
		else
			alpha="$(identify -format "%A " "${array[c]}" 2>/dev/null)"
			alpha="${alpha%% *}" # animated gifs return multiple values, we only need the 1st
		fi
		[[ "$alpha" != "False" ]] && alpha=1 # there's no harm in making this assumption
		shopt -u nocasematch
	fi

    if [[ "$size" == *x*x* ]]; then # more than one width x height means animated gif,which we do not want here
        magick "${array[c]}[0]" "$tmpdir/${array[c]##*/}" # extract the first frame
        array[c]="$tmpdir/${array[c]##*/}"
        size="$(identify -format "%wx%h" "${array[c]}")"
        animated=" (1st frame only)"
    else
        animated=""
    fi

	if [[ "$alpha" == 1 ]]; then
		# create temporary tile with bg color
        magick -size "$size" -background "$bgcol" xc:"$bgcol" "${array[c]}" -layers flatten "$tmptile"
	else
		tmptile="${array[c]}" # tmptile is only ever read, never written to, so
		# we can set it to the original image if it doesn't have an alpha channel
	fi

	#~title="Array $((c +1))/${#array[*]}: ${array[c]}\n$(file -b "${array[c]}")"

	# create a tiled preview canvas to be displayed with ueberzug
	magick -size "${width}x$height" tile:"$tmptile" "$preview"

	while true ; do
        screeninit "$((c +1))/${#array[*]}"
		printf "${PWD/$HOME/\~}/${array[c]#./} (${size}px)$animated\n"
		[[ $alpha == 1 ]] && printf "b:	Change tile background color (currently: $bgcol)\n"
		[[ $alpha != 1 ]] && [[ "$bg_setter" != "save" ]] && printf "o:	Set original file as desktop background, tiled\n"
		[[ $alpha == 1 ]] && printf "n:	Create new tile with BG color as seen, then $message\n"
		printf "s:	Submenu: create screen-sized canvas...\nr:	Redraw current preview\nR:	Restore previous (before starting $me) background\nSpace:	Next image\nBkspc:	Previous image\n.:	10 images forward\n,:	10 images backward\nD:	Delete current image\n0,q:	Quit\n"

		case $(prompt) in
			b)	if [[ $alpha == 1 ]]; then
					change_color "bgcol" # it's the name of the variable to be filled
					break
				else
					wrong_input
				fi
				;;
			o)	if [[ $alpha != 1 ]]; then
					set_bg tiled "${array[c]}"
					erase
				else
					wrong_input
				fi
				;;
			n)	if [[ $alpha == 1 ]]; then
					cp -f "$tmptile" "$output"
					set_bg tiled "$output"
					erase
				else
					wrong_input
				fi
				;;
			s)	full_size_manip
				;;
			r)	drawimage
				erase
				;;
			R)	set_bg restore
				erase
				;;
			0|q)
				exit_cleanup
				;;
			D)
                erase
				rm -i "${array[c]}"
                c=$((c + 1))
                break
				;;
			' ')
				c=$((c + 1))
                until [ -r "${array[c]}" ]; do c=$((c + 1)); done
				break
				;;
			) # backspace = previous image
				c=$((c - 1))
                until [ -r "${array[c]}" ]; do c=$((c - 1)); done
				break
				;;
			,) # 10 images backward
				c=$((c - 10))
                until [ -r "${array[c]}" ]; do c=$((c - 1)); done
				break
				;;
			.) # 10 images forward
				c=$((c + 10))
                until [ -r "${array[c]}" ]; do c=$((c + 1)); done
				break
				;;
			*)	wrong_input
				;;
		esac
	done
done

exit_cleanup
